﻿//#include <regx51.h>  // 引入51单片机头文件
//
//#define uchar unsigned char  // 定义uchar为unsigned char类型
//#define uint unsigned int    // 定义uint为unsigned int类型
//
//sbit K1 = P3 ^ 4;  // 用K1表示P3.4口
//sbit K2 = P3 ^ 5;  // 用K2表示P3.5口
//
//uchar unm[] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f };  // 数码管显示数字0~9的码值
//uchar t = 0, timer_tick = 0;  // 定义全局变量
//
//// ===== 1ms延时子程序 =====
//void delay(uint a) {
//    uchar j;
//    while (a--) {
//        for (j = 0; j < 120; j++);
//    }
//}
//
//// ===== 程序初始化 =====
//void Init() {
//    TMOD = 0x01;  // 设置定时器工作模式1
//    TH0 = 0x3C;   // 设置定时初值
//    TL0 = 0xB0;
//    IE = 0x82;    // 开启总中断和定时器0中断
//    P3_0 = 1;     // 关闭蜂鸣器
//}
//
//// ===== 数码管显示子程序 =====
//void display() {
//    P2 = 0xfe;          // 选通十位数码管
//    P0 = unm[t / 10];   // 显示十位数
//    delay(10);          // 延时10ms
//    P2 = 0xfd;          // 选通个位数码管
//    P0 = unm[t % 10];   // 显示个位数
//    delay(10);          // 延时10ms
//}
//
//// ===== 输出处理子程序 =====
//void shuchu() {
//    display();         // 调用数码管显示子程序
//    if (t == 5) {     // 判断是否计时5秒
//        P3_0 = 0;      // 蜂鸣器鸣叫
//    }
//}
//
//// ===== 去抖动子程序 =====
//// 功能：防止按键抖动造成误判，确认按键按下
//void qu_doudong() {
//    if (K1 == 0) {
//        do {
//            while (K1 == 1) {  // 判断K1是否按下，若未按下继续判断
//                display();     // 延时确认
//            }
//        } while (K1 == 1);
//
//        do {
//            while (K1 == 0) {  // 判断K1是否释放
//                display();     // 延时确认
//            }
//        } while (K1 == 0);
//    }
//
//    if (K2 == 0) {
//        do {
//            while (K2 == 1) {  // 判断K2是否按下
//                display();     // 延时确认
//            }
//        } while (K2 == 1);
//
//        do {
//            while (K2 == 0) {  // 判断K2是否释放
//                display();     // 延时确认
//            }
//        } while (K2 == 0);
//    }
//}
//
//// ===== 主程序 =====
//void main() {
//    Init();  // 初始化处理
//    while (1) {
//        shuchu();      // 输出处理
//        if (K1 == 0) { // 判断是否按下开始键
//            qu_doudong();  // 去抖动处理
//            t = 0;         // 清零计时
//            timer_tick = 0;
//            TH0 = 0x3C;    // 设置定时器初值
//            TL0 = 0xB0;
//            TR0 = 1;       // 开启定时器
//            P3_0 = 1;      // 关闭蜂鸣器
//        }
//        if (K2 == 0) { // 判断是否按下停止键
//            qu_doudong();  // 去抖动处理
//            TR0 = 0;       // 关闭定时器
//            t = 0;         // 清零计时
//            P3_0 = 1;      // 关闭蜂鸣器
//        }
//    }
//}
//
//// ===== 定时器中断子程序 =====
//void timer0_server() interrupt 1 {
//    TH0 = 0x3C;    // 重装定时初值
//    TL0 = 0xB0;
//    timer_tick++;  // 计数器加1
//    if (timer_tick == 20) {
//        timer_tick = 0;  // 清零计数器
//        t++;             // 计时变量加1
//    }
//    if (t == 5) {
//        TR0 = 0;   // 关闭定时器
//    }
//}


这段程序的主要功能是通过 DS1302实时时钟模块 和 共阴极数码管 显示当前的时间（时分秒），并实现动态刷新，以便在数码管上显示不断更新的时间信息。以下是代码的分解及其功能说明，帮助您理解并写出实验报告。
1. 程序所需头文件
#include "public.h"
#include "smg.h"
#include "ds1302.h"
这里包含了头文件 public.h、smg.h 和 ds1302.h，它们可能定义了硬件端口的宏和一些常量、变量或函数声明，确保代码可以正常编译和访问底层硬件。
2. 段码数组 gsmg_code
u8 gsmg_code[17] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
                    0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71 };
数组 gsmg_code 存储了共阴极数码管显示数字 0 到 F 的段码数据，方便在数码管上显示不同的数字或字母。
3. DS1302的读写地址命令
u8 gREAD_RTC_ADDR[7] = { 0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d };
u8 gWRITE_RTC_ADDR[7] = { 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c };
这两个数组存储了DS1302 RTC芯片的读写地址。分别用于读取和写入年、月、日、时、分、秒等时间信息。
4. 初始化时间数组 gDS1302_TIME
u8 gDS1302_TIME[7] = { 0x47, 0x51, 0x13, 0x20, 0x04, 0x05, 0x21 };
gDS1302_TIME 是一个初始化时间的数组（BCD编码），该数组代表年、月、日、时、分、秒等信息，可以作为初始时间写入DS1302。
5. 延时函数
void delay_10us(u16 ten_us) { while (ten_us--); }
void delay_ms(u16 ms) {
    u16 i, j;
    for (i = ms; i > 0; i--)
        for (j = 110; j > 0; j--);
}
延时函数用于控制数码管刷新速度。delay_10us 用于短时间延迟，delay_ms 用于较长的毫秒级延迟。
6. 数码管动态显示函数 smg_display
void smg_display(u8 dat[], u8 pos) {
    ...
}
该函数实现动态数码管显示。其功能如下：
•	根据参数 dat[] 和 pos，将指定位置的段码数据输出到数码管。
•	使用 LSA、LSB 和 LSC 控制选通信号，依次点亮数码管的各个位置。
•	delay_10us 保持显示一段时间后将 SMG_A_DP_PORT 置零，实现动态显示效果。
7. DS1302写入单字节函数 ds1302_write_byte
void ds1302_write_byte(u8 addr, u8 dat) {
    ...
}
该函数用于向DS1302写入单字节数据。流程如下：
•	发送启动信号，将 RST 置高。
•	使用 CLK 时钟信号，将 addr 和 dat 的数据逐位写入 IO 引脚。
•	将 RST 置低以结束写入。
8. DS1302读取单字节函数 ds1302_read_byte
u8 ds1302_read_byte(u8 addr) {
    ...
}
该函数用于从DS1302读取单字节数据。流程如下：
•	发送启动信号，并通过 CLK 时钟信号，先写入地址。
•	然后逐位读取数据，将接收到的值存入 value 中。
•	关闭 RST 引脚以结束读操作，并返回读取的数据。
9. DS1302初始化函数 ds1302_init
void ds1302_init(void) {
    ...
}
该函数在DS1302上进行初始时间设置。过程如下：
•	关闭写保护 0x8E 地址，使DS1302可以写入。
•	通过 gWRITE_RTC_ADDR 的地址写入 gDS1302_TIME 的初始时间数据。
•	写入完成后，重新设置写保护。
10. DS1302读取时间函数 ds1302_read_time
void ds1302_read_time(void) {
    ...
}
该函数依次读取 年、月、日、时、分、秒 的时间数据，并更新到 gDS1302_TIME 数组中。
11. 主函数 main
void main() {
    ...
}
主函数流程如下：
•	调用 ds1302_init() 初始化DS1302时间。
•	进入 while (1) 循环，不断执行以下操作：
1.	调用 ds1302_read_time()，从DS1302读取当前时间。
2.	将 gDS1302_TIME 数组中存储的时分秒数据转换为段码存储到 time_buf 数组中。
3.	调用 smg_display(time_buf, 1)，动态显示时间数据。
time_buf 数组内容说明
time_buf 将小时、分钟和秒分别显示为两位数，并用 0x40 代表分隔符（通常是小数点）来显示时间格式。例如：
•	time_buf[0] 和 time_buf[1] 是小时的高位和低位。
•	time_buf[3] 和 time_buf[4] 是分钟的高位和低位。
•	time_buf[6] 和 time_buf[7] 是秒的高位和低位。
程序运行总结
整个程序的设计是一个基于数码管的动态显示系统，显示DS1302实时时钟模块的时间。通过合理使用定时和动态显示，代码实现了低功耗、快速响应的数字时钟显示功能。

